#!/usr/bin/python
# -*- coding: UTF-8 -*-
'''
Python 中 # 的作用包括两个主要方面：1、用作代码注释；2、在特定环境下用作指令标识
在Python脚本的首行，#可以用作Shebang，这是在Unix系统中用来指示脚本执行所需解释器的特殊注释行。它由一串特定的字符组成，以#!开头，其后紧跟解释器的绝对路径。
'''

import sys

import requests
from decorator.class_logger_decorator import ClassLoggerDecorator
from decorator.func_log_decorator import func_log_decorator, func_log_decorator_with_params, func_log_with_params_decorator, multi_decorator1, multi_decorator2, multi_decorator3
from util.log_util import LogUtil
from entity.student import Student
from entity.address import Address, PropertyTest
from entity.animal import Cake, Dog, Cat
from dao.student_dao import StudentDao
from dao.address_dao import AddressDao
from util.run_time_file_recorder import RunTimeFileRecorder
from network.api_model import ApiModel, ApiHeaderType
from network.api_exception import ApiException
import time
from util.type_list import TypedList
import util.which_systime as which_system
from collections import namedtuple
from functools import reduce
import re
from bs4 import BeautifulSoup

# # 查看模块中的内容
# print(dir(time))

# 示例代码：文档化函数
def calculate_area(length, width):
    """计算矩形面积。

    Args:
        length (float): 矩形的长度
        width (float): 矩形的宽度

    Returns:
        float: 矩形的面积
    """
    return length * width

# 获取系统信息
which_system.print_which_system_1()
which_system.print_which_system_2()

# 测试日志
# import util.log_util
# util.log_util.logger.info('>>>>> 打印日志测试')
LogUtil.info('>>>>> 打印日志测试')

# 自定义限制类型的List
try:
    nList = list([1,2,3])
    nList.append('a')
    intList = TypedList(int, [1,2,3])
    intList.append('a')
except Exception as e:
    print(f'出错了：{e}')

# 元组测试
tup0 = (0, 1, 2)
tup1 = ('a', 'b', 'c')
tup2 = tup0 + tup1
print(f'元组tup2: {tup2}, tup2[4]={tup2[4]}')
# 命名元组
# 定义一个命名元组
NameTuple = namedtuple('NameTuple', ['name', 'age', 'email'])
# 创建一个命名元组的实例
ntup0 = NameTuple(name='Alice', age=30, email='alice@example.com')
print(f"ntup0.name={ntup0.name}, ntup0.age={ntup0.age}")
# 遍历命名元组
for key, value in ntup0._asdict().items():
    print(f"{key}: {value}")

# 迭代器
it = iter(ntup0)
print(f"next(it): {next(it)}")
# while True:
#     try:
#         print (next(it))
#     except StopIteration:
#         sys.exit()

# 生成器
def countdown(n):
    while n > 0:
        # yield 是一个关键字，用于定义生成器函数，生成器函数是一种特殊的函数，可以在迭代过程中逐步产生值，而不是一次性返回所有结果
        yield n
        n -= 1
# 创建生成器对象
generator = countdown(5)
# 通过迭代生成器获取值
print(next(generator))  # 输出: 5
print(next(generator))  # 输出: 4
print(next(generator))  # 输出: 3
# 使用 for 循环迭代生成器
for value in generator:
    print(value)  # 输出: 2 1

# lamba 匿名函数
# lambda 函数是匿名的，它们没有函数名称，只能通过赋值给变量或作为参数传递给其他函数来使用。
# lambda 函数通常只包含一行代码，这使得它们适用于编写简单的函数。
# lambda 语法格式: lambda arguments: expression
fstr = lambda: "Hello, world!"
fa10 = lambda a : a + 10
fadd = lambda a, b : a * b
numbers = [1, 2, 3, 4, 5]
# 使用 reduce() 和 lambda 函数计算乘积
pn = reduce(lambda x, y: x * y, numbers)
print(pn)  # 输出：120

# 字典测试
tinydict = {'Name': 'Zara', 'Age': 7, 'Class': 'First'}
tinydict['Friend'] = 'Lufy'
tinydict['Name'] = 'Zero'
del tinydict['Class']
print(f"tinydict:{str(tinydict)}")
tinydict.clear()

# set测试
set0 = {3, 4, 8, 9}
set1 = {1, 2, 3, 4}
set1.add(6)
set1.remove(1)
set3 = set0.union(set1)
isExistTwo = 2 in set3
print(f"set3:{set3}, isExist2: {isExistTwo}")

# 推导式
# Python 推导式是一种独特的数据处理方式，可以从一个数据序列构建另一个新的数据序列的结构体。

# 列表推导式
# [表达式 for 变量 in 列表] 
# [out_exp_res for out_exp in input_list]
# 或者 
# [表达式 for 变量 in 列表 if 条件]
# [out_exp_res for out_exp in input_list if condition]
names = ['Bob','Tom','alice','Jerry','Wendy','Smith']
new_names = [name.upper()for name in names if len(name)>3]
multiples = [i for i in range(30) if i % 3 == 0]
# 字典推导式
# { key_expr: value_expr for value in collection }
# 或
# { key_expr: value_expr for value in collection if condition }
listdemo = ['Google','Runoob', 'Taobao']
newdict = {key:len(key) for key in listdemo} # 将列表中各字符串值为键，各字符串的长度为值，组成键值对
newdict2 = {x: x**2 for x in (2, 4, 6)}
# 集合推导式
# { expression for item in Sequence }
# 或
# { expression for item in Sequence if conditional }
setnew = {i**2 for i in (1,2,3)}
# 元组推导式（生成器表达式）
# 元组推导式可以利用 range 区间、元组、列表、字典和集合等数据类型，快速生成一个满足指定需求的元组。
a = (x for x in range(1,10)) # <generator object <genexpr> at 0x7faf6ee20a50>  # 返回的是生成器对象
tuple(a) # 使用 tuple() 函数，可以直接将生成器对象转换成元组

# 测试文件写入
RunTimeFileRecorder.record()

# 测试面向对象
stu1 = Student()
stu2 = Student.fromClassNameAge("LiLei", 20)

print(f"...stu1 is {type(stu1)}")

stu1Info = "stu1: -name:%s -age:%d" % (stu1.name, stu1.age)
LogUtil.info(stu1Info)
stu2Info = "stu2: -name:{name} -age:{age}".format(name=stu2.name, age=stu2.age)
LogUtil.info(stu2Info)
LogUtil.info("stu3: -name: " + stu2.name + " -age: " + str(stu2.age))

# Sqlite测试
StudentDao.insertOrUpdate(stu2)
studentList = StudentDao.query()
LogUtil.info("student total : {len}".format(len=len(studentList)))
# @dataclass测试
addr = Address(id=0, province="福建省", city= "泉州市", disctrict= "丰泽区")
AddressDao.insertOrUpdate(addr)
addrList = AddressDao.query()
LogUtil.info("address total: {len}".format(len=len(addrList)))
# @property测试
propertyTest = PropertyTest()
LogUtil.info(propertyTest.method_with_property)
LogUtil.info(propertyTest.method_without_property())
propertyTest.name = "ChangeName"
LogUtil.info(propertyTest.name)
LogUtil.info(f'propertyTest._name: {propertyTest._name}')

# 继承测试
# 单继承
dog = Dog()
cat = Cat()
dog.walk()
cat.walk()
# 多继承
cake = Cake()
LogUtil.info(f"cake id:{cake.id}, isSall:{cake.isSall}")

# 装饰器测试
# 函数装饰器，装饰无参数的方法
@func_log_decorator
def sayHello():
    time.sleep(1)
    print("Hello, world!")
sayHello()
# 函数装饰器，装饰带参数的方法
@func_log_with_params_decorator
def sayWithName(name):
    time.sleep(1)
    print(f"Hello {name}!")
sayWithName("Leizi")
# 函数装饰器，带参数的装饰器
@func_log_decorator_with_params(log_level="DEBUG")
def sayHello2():
    print("Hello world 222")
sayHello2()
# 多个装饰器
@multi_decorator1
@multi_decorator2
@multi_decorator3
def sayHello3():
    print("Hello world 333")
sayHello3()
# 类装饰器
@ClassLoggerDecorator
def sayHello5():
    print("Hello world 555")
sayHello5()

# 测试Http请求封装，自定义异常
print(f"ApiContentTypeHeader.json.name: {ApiHeaderType.json.name},\nApiContentTypeHeader.json.value: {ApiHeaderType.json.value}")
try:
    params1 = {"username": "yangxuesong@eiker.com.cn"}
    res1 = ApiModel.getInstance().get("https://publish.icheer.cn/prod-api/admin-api/system/product/lastVersion?code=namy160", params=params1)
    print(f"res1.data:{res1.data}")
    params2 = {"deviceCode": 16000000, "language": "en"}
    res2 = ApiModel.getInstance().post("https://config.apps.icheer.cn/admin-api/system/skin-config/listConfigs", params=params2)
    print(f"res2.data:{res2.data}")
    res3 = ApiModel.getInstance().get("https://abc_config.apps.icheer.cn/admin-api/system/skin-config/listConfigs")
    print(f"res2.data:{res3.data}")
except ApiException as e:
    print(f"发生了ApiException异常：\n{e.msg}")

# 正则表达式
# re.match从起始位置匹配
print(re.match('www', 'www.runoob.com').span())  # 在起始位置匹配 (0, 3)
print(re.match('com', 'www.runoob.com'))         # 不在起始位置匹配 None
# e.search 扫描整个字符串并返回第一个成功的匹配
print(re.search('www', 'www.runoob.com').span())  # 匹配 (0, 3)
print(re.search('com', 'www.runoob.comcom').span())  # 匹配 (11, 14)

line = "Cats are smarter than dogs"
# .* 表示任意匹配除换行符（\n、\r）之外的任何单个或多个字符
# (.*?) 表示"非贪婪"模式，只保存第一个匹配到的子串
matchObj = re.match(r'(.*) are (.*?) .*', line, re.M|re.I) # re.match(pattern, string, flags=0)
searchObj = re.search(r'(.*) are (.*?) .*', line, re.M|re.I) # re.search(pattern, string, flags=0)
# group(num=0) 	匹配的整个表达式的字符串，group() 可以一次输入多个组号，在这种情况下它将返回一个包含那些组所对应值的元组。
# groups() 返回一个包含所有小组字符串的元组，从 1 到 所含的小组号。
print(f"matchObj.group():{matchObj.group()}, matchObj.groups():{matchObj.groups()}, searchObj.group():{searchObj.group()}, searchObj.groups():{searchObj.groups()}")

# re.sub用于替换字符串中的匹配项 re.sub(pattern, repl, string, count=0, flags=0)
phone = "2004-959-559 # 这是一个电话号码"
# 删除注释
num = re.sub(r'#.*$', "", phone) #2004-959-559
print ("电话号码 : ", num)
# 移除非数字的内容
num = re.sub(r'\D', "", phone) #2004959559
print ("电话号码 : ", num)
# repl为函数的情况
def double(matched): # 将匹配的数字乘以 2
    value = int(matched.group('value'))
    return str(value * 2)
s = 'A23G4HFD567'
print(re.sub('(?P<value>\d+)', double, s)) # A46G8HFD1134

# re.compile 函数用于编译正则表达式，生成一个正则表达式（ Pattern ）对象，供 match() 和 search() 这两个函数使用
pattern = re.compile(r'\d+')
print(pattern.match('a100'))
print(pattern.search('a100'))

# findall 在字符串中找到正则表达式所匹配的所有子串，并返回一个列表，如果有多个匹配模式，则返回元组列表，如果没有找到匹配的，则返回空列表
# re.findall(pattern, string, flags=0)
# 或
# pattern.findall(string[, pos[, endpos]])
result1 = re.findall(r'\d+','runoob 123 google 456') # ['123', '456']
pattern = re.compile(r'\d+')
result2 = pattern.findall('runoob 123 google 456') # ['123', '456']
result3 = pattern.findall('run88oob123google456', 0, 10) # ['88', '12']

# re.finditer 和 findall 类似，在字符串中找到正则表达式所匹配的所有子串，并把它们作为一个迭代器返回。
# re.finditer(pattern, string, flags=0)
it = re.finditer(r"\d+","12a32bc43jf3") 
for match in it: 
    print (match.group() )

# re.split 按照能够匹配的子串将字符串分割后返回列表，它的使用形式如下
# re.split(pattern, string[, maxsplit=0, flags=0])
print(re.split(r'\W+', 'runoob, runoob, runoob.')) #\W 匹配任何非单词字符。等价于 '[^A-Za-z0-9_]'

# 正则表达式对象
# re.RegexObject
#   re.compile() 返回 RegexObject 对象。
# re.MatchObject
#   group() 返回被 RE 匹配的字符串。
#     start() 返回匹配开始的位置
#     end() 返回匹配结束的位置
#     span() 返回一个元组包含匹配 (开始,结束) 的位置

# 可选标志 flag
# re.IGNORECASE 或 re.I 使匹配对大小写不敏感	
# re.MULTILINE 或 re.M 多行匹配，影响 ^ 和 $，使它们匹配字符串的每一行的开头和结尾
# re.DOTALL 或 re.S 使 . 匹配包括换行符在内的任意字符
# re.ASCII 使 \w, \W, \b, \B, \d, \D, \s, \S 仅匹配 ASCII 字符
# re.VERBOSE 或 re.X 忽略空格和注释，可以更清晰地组织复杂的正则表达式

# re.X 例子
pattern = re.compile(r'''
    \d+  # 匹配数字
    [a-z]+  # 匹配小写字母
''', flags=re.VERBOSE)
result = pattern.match('123abc##')
print(result.group())  # 输出: '123abc'

# BeautifulSoup检索网页的a标签
resp = requests.get("http://www.poycode.cn/category/coding/python/")
print(resp.status_code)
soup = BeautifulSoup(resp.content)
# 格式化html输出
# print(soup.prettify())
# 获取所有class='entry-title'的h2标签
tags = soup.find_all('h2', class_='entry-title')
for tag in tags:
    a_tag = tag.find('a')
    print('Title:[%s], URL:[%s]' %(tag.text, a_tag['href']))